package org.ctstudio.oa.duty.service;

import java.time.LocalDate;
import java.time.LocalTime;
import java.util.List;

import org.ctstudio.common.datatype.TimeFragment;
import org.ctstudio.oa.duty.bean.DutyDefine;
import org.ctstudio.oa.duty.bean.DutyRecord;
import org.ctstudio.oa.duty.bean.LeaveApply;
import org.ctstudio.oa.duty.dao.DutyDefineMapper;
import org.ctstudio.oa.duty.dao.DutyRecordsMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class DutyCalcService {
  @Autowired
  public DutyRecordsMapper mapper;
  @Autowired
  public DutyDefineMapper dutyDefMapper;

  public void calcDutyTime(DutyRecord duty) {
    DutyDefine dutyDef = dutyDefMapper.getDutyDefine();
    List<TimeFragment> breaksDef = dutyDefMapper.getBreaks();

    // 午餐时间
    TimeFragment lunchBreakDef = breaksDef.get(0);

    // 晚饭以后的时间算加班
    TimeFragment lastBreak = breaksDef.get(breaksDef.size() - 1);
    LocalTime overTimeStart = lastBreak.getTo().isAfter(dutyDef.getEndTime()) ? lastBreak.getTo() : dutyDef.getEndTime();
    TimeFragment overTimeDef = new TimeFragment(overTimeStart, LocalTime.MAX);

    calcDutyTime(dutyDef, lunchBreakDef, overTimeDef, duty);
  }

  public void calcDutyTime(DutyDefine dutyDef, TimeFragment lunchBreakDef, TimeFragment overTimeDef, DutyRecord duty) {
    // 计算正常上班时间
    TimeFragment dutyTime = new TimeFragment(duty.getSignInTime(), duty.getSignOutTime());
    TimeFragment work = dutyTime.intersect(TimeFragment.class, new TimeFragment(dutyDef.getStartTime(), dutyDef.getEndTime()));
    TimeFragment lunchBreak = dutyTime.intersect(TimeFragment.class, lunchBreakDef);
    duty.setWorkTimeSecond(TimeFragment.getPeriodSecond(work) - TimeFragment.getPeriodSecond(lunchBreak));
    // 计算加班时间
    TimeFragment overTime = dutyTime.intersect(TimeFragment.class, overTimeDef);
    duty.setOverTimeSecond(TimeFragment.getPeriodSecond(overTime));

    duty.setLate(isLate(dutyDef.getFlexStartTime(), duty.getSignInTime()));
    duty.setEarly(isEarly(dutyDef.getEndTime(), duty.getSignOutTime()));
  }

  private boolean isLate(LocalTime expected, LocalTime actual) {
    if (expected == null || actual == null) {
      return false;
    }
    // 允许晚刷卡5分钟
    return expected.isBefore(actual);
  }

  private boolean isEarly(LocalTime expected, LocalTime actual) {
    if (expected == null || actual == null) {
      return false;
    }
    return expected.isAfter(actual);
  }

  /**
   * 计算有效工时和加班时间
   * 
   * @param list
   */
  public void calcWorkTime2(List<DutyRecord> list) {
    DutyDefine dutyDef = dutyDefMapper.getDutyDefine();
    List<TimeFragment> breaksDef = dutyDefMapper.getBreaks();

    // 午餐时间
    TimeFragment lunchBreakDef = breaksDef.get(0);

    // 晚饭以后的时间算加班
    TimeFragment lastBreak = breaksDef.get(breaksDef.size() - 1);
    LocalTime overTimeStart = lastBreak.getTo().isAfter(dutyDef.getEndTime()) ? lastBreak.getTo() : dutyDef.getEndTime();
    TimeFragment overTimeDef = new TimeFragment(overTimeStart, LocalTime.MAX);
    for (DutyRecord onOff : list) {
      calcDutyTime(dutyDef, lunchBreakDef, overTimeDef, onOff);
    }
  }

  public void refreshDuty(LocalDate signDate, String uid) {
    DutyRecord n = mapper.fromSignRecord(signDate, uid);
    DutyRecord exists = mapper.get(signDate, uid);
    if (exists == null) {
      mapper.add(n);
    } else {
      calcDutyTime(n);
      mapper.editSignTime(n);
    }
  }

  /**
   * 计算请假加班时长
   * 
   * @param apply
   */
  public void calcLeaveTime(LeaveApply apply) {
    DutyDefine dutyDef = dutyDefMapper.getDutyDefine();
    List<TimeFragment> breaksDef = dutyDefMapper.getBreaks();
    long days = apply.getToDate().toEpochDay() - apply.getFromDate().toEpochDay();
    long leaveSecond = 0;
    if (days == 0) {
      TimeFragment leaveRange = new TimeFragment(apply.getFromTime(), apply.getToTime());
      leaveSecond = calcWorkTimeSecond(leaveRange, dutyDef, breaksDef);
    } else {
      TimeFragment firstDay = new TimeFragment(apply.getFromTime(), LocalTime.MAX);
      TimeFragment lastDay = new TimeFragment(LocalTime.MIN, apply.getToTime());
      TimeFragment midDay = new TimeFragment(LocalTime.MIN, LocalTime.MAX);
      leaveSecond += calcWorkTimeSecond(firstDay, dutyDef, breaksDef);
      leaveSecond += calcWorkTimeSecond(lastDay, dutyDef, breaksDef);
      leaveSecond += calcWorkTimeSecond(midDay, dutyDef, breaksDef) * (days - 1);
    }
    apply.setTotalTimeSecond(leaveSecond);
  }

  private long calcWorkTimeSecond(TimeFragment timeRange, DutyDefine dutyDef, List<TimeFragment> breaksDef) {
    TimeFragment dutyRange = new TimeFragment(dutyDef.getStartTime(), dutyDef.getEndTime());
    TimeFragment actual = timeRange.intersect(TimeFragment.class, dutyRange);
    long actualSecond = actual == null ? 0 : actual.getPeriodSecond();
    for (TimeFragment breakDef : breaksDef) {
      TimeFragment actualBreak = actual.intersect(TimeFragment.class, breakDef);
      actualSecond -= actualBreak == null ? 0 : actualBreak.getPeriodSecond();
    }
    return actualSecond;
  }
}